package org.truenewx.tnxjee.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.annotation.*;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.truenewx.tnxjee.core.Strings;
import org.truenewx.tnxjee.core.caption.CaptionUtil;
import org.truenewx.tnxjee.core.ref.ReferenceCounter;
import org.truenewx.tnxjee.model.entity.Entity;
import org.truenewx.tnxjee.service.Service;
import org.truenewx.tnxjee.service.ServiceFactory;
import org.truenewx.tnxjee.service.exception.BusinessException;
import org.truenewx.tnxjee.service.handler.EntityReferenceCountable;

/**
 * 服务支持，具有快速获取其它服务的能力
 *
 * @author jianglei
 */
public abstract class ServiceSupport {

    @Autowired
    private ServiceFactory serviceFactory;
    private List<EntityReferenceCountable> entityReferenceCountableList = new ArrayList<>();

    @Autowired
    public void setEntityReferenceCountableProvider(
            ObjectProvider<EntityReferenceCountable> entityReferenceCountableProvider) {
        entityReferenceCountableProvider.iterator().forEachRemaining(
                entityReferenceCountable -> this.entityReferenceCountableList.add(entityReferenceCountable));
    }

    protected final <S extends Service> S getService(Class<S> serviceClass) {
        S service = this.serviceFactory.getService(serviceClass, false); // 默认取非事务性服务
        if (service == null) { // 如果没有非事务性服务，则取事务性服务
            service = this.serviceFactory.getService(serviceClass, true);
        }
        return service;
    }

    @Nonnull
    protected final Locale getLocale() {
        return LocaleContextHolder.getLocale();
    }

    /**
     * 计算指定实体对象的引用数
     *
     * @param entity 实体对象
     * @return 指定实体对象的引用计数器
     */
    protected final ReferenceCounter countReferences(Entity entity) {
        return countReferences(entity, false);
    }

    /**
     * 计算指定实体对象的引用数
     *
     * @param entity   被引用实体对象
     * @param deleting 是否删除场景中，在删除场景中可能存在特殊处理
     * @return 指定实体对象的引用计数器
     */
    private ReferenceCounter countReferences(Entity entity, boolean deleting) {
        ReferenceCounter counter = new ReferenceCounter();
        for (EntityReferenceCountable entityReferenceCountable : this.entityReferenceCountableList) {
            entityReferenceCountable.countReferences(entity, counter, deleting);
        }
        return counter;
    }

    /**
     * 校验指定实体对象的可删除性，默认检查指定实体对象是否被引用，如果被引用则抛出业务异常。子类可覆写以实现更复杂的校验规则
     *
     * @param entity 被校验的实体对象
     */
    protected void validateDelete(Entity entity) {
        if (entity != null) {
            ReferenceCounter counter = countReferences(entity, true);
            if (counter.exists()) {
                String caption = CaptionUtil.getCaption(entity.getClass(), getLocale());
                if (caption == null) {
                    caption = Strings.EMPTY;
                }
                throw new BusinessException(getReferencedEntityErrorCode(), caption);
            }
        }
    }

    /**
     * @return 被引用的实体对象不可删除的错误码。子类可覆写以提供不同的错误消息
     */
    protected String getReferencedEntityErrorCode() {
        return "error.tnxjee.service.referenced_entity_cannot_be_deleted";
    }

}
